package com.hog.utils.date;

import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @author 唐晴
 * @date 2020年08月27日
 * @since JDK 1.8
 */
public class SimpleLunarCalendar {
    private static final long minTimeInMillis = -2206425952001L;
    private static final long maxTimeInMillis = 4102416000000L;
    private static final int[] lunarInfo = new int[]{19416, 19168, 'ꕰ', 21717, '퉠', '\ud950', 21844, 22191, '髐', 21970, 19168, 'ꖶ', 'ꓐ', '퉐', '튕', '땏', '횠', '궢', '閰', 18807, 18815, '꒰', '뒵', 27216, 27968, '\uab54', 11119, '镰', 21234, 18800, 25958, '풠', '\uea50', 27285, 23263, 11104, '蛣', '鋯', '죗', '쥟', '풠', '\ud8a6', '땟', 22176, 'ꖴ', 9695, '鋐', '튲', 'ꥐ', '땗', 27808, '땐', 21333, 19887, 'ꖰ', 17779, 21183, 'ꦨ', '\ue950', 27296, '꺦', '\uab50', 19296, 'ꫤ', 'ꕰ', 21088, '\uf263', '\ud950', 23383, 22176, '雐', 19925, 19152, 'ꓐ', '퓔', '퉐', '하', '땀', '뚠', '閦', '閿', 18864, 'ꥴ', '꒰', '뉺', 27216, 27968, '꽆', '\uab60', '镰', 19189, 18800, 25776, 29859, '\uea50', 27480, 23232, '\uab60', '雕', '鋠', '쥠', '\ud954', '풠', '\uda50', 30034, 22176, '\uabb7', 9680, '鋐', '쪵', 'ꥐ', '뒠', '몤', '교', 21977, 19360, 'ꖰ', 20854, 21183, 'ꤰ', 31060, 27296, '교', 23378, 19296, 'ꛦ', 'ꓠ', '퉠', '\uea65', '픰', 23200, 30371, '雐', 19195, 19152, 'ꓐ', '킶', '퉟', '픠', '\udd45', '떠', 22224, 21938, 18864, 'ꕷ', '꒰', '꩐', '뉕', 27951, '궠', 19299, '鍿', 18936, 18800, 25776, 26790, '\uea5f', 27424, 'ꛄ', 'ꫯ', '鋠', '틣', '쥠', '핗', '풠', '\uda50', 23893, 22176, 'ꛐ', 21972, 21200, 'ꦸ', 'ꥐ', '뒠', '뚦', '교', 21920, '\uaba4', 'ꖰ', 21168, '뉳', 26928, 29495, 27296, '교', 19285, 19311, 'ꕰ', 21732, '퉠', '\ue968', '픠', '\udaa0', 27302, 22239, 19168, '꧔', 'ꓐ', '텐', '\uf252', '픠'};
    private static final String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"};
    private static final String[] lunarString1 = new String[]{"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
    private static final String[] lunarString2 = new String[]{"初", "十", "廿", "卅", "正", "腊", "冬", "闰"};
    private int lunarYear;
    private int lunarMonth;
    private int lunarDay;
    private boolean isLeap;
    private boolean isLeapYear;
    private int maxDayInMonth = 29;

    public SimpleLunarCalendar(long TimeInMillis) {
        this.init(TimeInMillis);
    }

    public SimpleLunarCalendar(Date date) {
        if(date == null) {
            date = new Date();
        }

        this.init(date.getTime());
    }

    private void init(long timeInMillis) {
        if(timeInMillis > -2206425952001L && timeInMillis < 4102416000000L) {
            GregorianCalendar baseDate = new GregorianCalendar(1900, 0, 31);
            long offset = (timeInMillis - baseDate.getTimeInMillis()) / 86400000L;
            this.lunarYear = 1900;

            for(int daysInLunarYear = getLunarYearDays(this.lunarYear); this.lunarYear < 2100 && offset >= (long)daysInLunarYear; daysInLunarYear = getLunarYearDays(++this.lunarYear)) {
                offset -= (long)daysInLunarYear;
            }

            int leapMonth = getLunarLeapMonth(this.lunarYear);
            this.isLeapYear = leapMonth > 0;
            int lunarMonth = 1;
            boolean isDecrease = true;
            boolean isLeap = false;
            int daysInLunarMonth = 0;

            label62:
            while(true) {
                while(true) {
                    if(lunarMonth >= 13 || offset <= 0L) {
                        break label62;
                    }

                    if(isLeap && !isDecrease) {
                        daysInLunarMonth = getLunarLeapDays(this.lunarYear);
                        isDecrease = true;
                    } else {
                        daysInLunarMonth = getLunarMonthDays(this.lunarYear, lunarMonth);
                    }

                    if(offset < (long)daysInLunarMonth) {
                        break label62;
                    }

                    offset -= (long)daysInLunarMonth;
                    if(leapMonth == lunarMonth && !isLeap) {
                        isDecrease = false;
                        isLeap = true;
                    } else {
                        ++lunarMonth;
                    }
                }
            }

            this.maxDayInMonth = daysInLunarMonth != 0?daysInLunarMonth:getLunarMonthDays(this.lunarYear, lunarMonth);
            this.lunarMonth = lunarMonth;
            this.isLeap = lunarMonth == leapMonth && isLeap;
            this.lunarDay = (int)offset + 1;
        }

    }

    private static int getLunarYearDays(int lunarYear) {
        int daysInLunarYear = 348;

        for(int i = '耀'; i > 8; i >>= 1) {
            daysInLunarYear += (lunarInfo[lunarYear - 1900] & i) != 0?1:0;
        }

        daysInLunarYear += getLunarLeapDays(lunarYear);
        return daysInLunarYear;
    }

    private static int getLunarLeapDays(int lunarYear) {
        return getLunarLeapMonth(lunarYear) > 0?((lunarInfo[lunarYear - 1899] & 15) == 15?30:29):0;
    }

    private static int getLunarLeapMonth(int lunarYear) {
        int leapMonth = lunarInfo[lunarYear - 1900] & 15;
        leapMonth = leapMonth == 15?0:leapMonth;
        return leapMonth;
    }

    private static int getLunarMonthDays(int lunarYear, int lunarMonth) {
        int daysInLunarMonth = (lunarInfo[lunarYear - 1900] & 65536 >> lunarMonth) != 0?30:29;
        return daysInLunarMonth;
    }

    private static String getLunarYearString(int lunarYear) {
        String lunarYearString = "";
        String year = String.valueOf(lunarYear);

        for(int i = 0; i < year.length(); ++i) {
            char yearChar = year.charAt(i);
            int index = Integer.parseInt(String.valueOf(yearChar));
            lunarYearString = lunarYearString + lunarString1[index];
        }

        return lunarYearString;
    }

    private static String getLunarMonthString(int lunarMonth) {
        String lunarMonthString = "";
        if(lunarMonth == 1) {
            lunarMonthString = lunarString2[4];
        } else {
            if(lunarMonth > 9) {
                lunarMonthString = lunarMonthString + lunarString2[1];
            }

            if(lunarMonth % 10 > 0) {
                lunarMonthString = lunarMonthString + lunarString1[lunarMonth % 10];
            }
        }

        return lunarMonthString;
    }

    private static String getLunarDayString(int lunarDay) {
        if(lunarDay >= 1 && lunarDay <= 30) {
            int i1 = lunarDay / 10;
            int i2 = lunarDay % 10;
            String c1 = lunarString2[i1];
            String c2 = lunarString1[i2];
            if(lunarDay < 11) {
                c1 = lunarString2[0];
            }

            if(i2 == 0) {
                c2 = lunarString2[1];
            }

            return c1 + c2;
        } else {
            return "";
        }
    }

    public String getAnimalString() {
        return this.lunarYear == 0?null:Animals[(this.lunarYear - 4) % 12];
    }

    public String getDayString() {
        return this.lunarDay == 0?null:getLunarDayString(this.lunarDay);
    }

    public String getMonthString() {
        return this.lunarMonth == 0?null:(this.isLeap()?"闰":"") + getLunarMonthString(this.lunarMonth);
    }

    public String getYearString() {
        return this.lunarYear == 0?null:getLunarYearString(this.lunarYear);
    }

    public String getDateString() {
        return this.lunarYear == 0?null:this.getYearString() + "年" + this.getMonthString() + "月" + this.getDayString() + "日";
    }

    public boolean isLeap() {
        return this.isLeap;
    }

    public boolean isLeapYear() {
        return this.isLeapYear;
    }

    public boolean isBigMonth() {
        return this.getMaxDayInMonth() > 29;
    }

    public int getMaxDayInMonth() {
        return this.lunarYear == 0?0:this.maxDayInMonth;
    }

    public int getDay() {
        return this.lunarDay;
    }

    public int getMonth() {
        return this.lunarMonth;
    }

    public int getYear() {
        return this.lunarYear;
    }
}